
#if defined(CONFIG_TAR4ESP32)
#include "esp_netif.h"
#include "sdkconfig.h"
#endif

#include <comm_handler.hpp>
#include <tcp_client.hpp>
#include <utils.hpp>

static TcpClient* s_TcpClient = nullptr;

TcpClient* TcpClient::Instance(void)
{
	if (s_TcpClient == nullptr)
	{
		s_TcpClient = new TcpClient();
	}
	return s_TcpClient;
}

TcpClient::TcpClient()
	: TaskRun(PRIORITY_TCPCLIENT, 5500, "tcp_client")
	, m_Ip(SERVER_IP)
	, m_Port(TCP_PORT)
	, m_SocketId(0)
	, m_SizeSend(0)
	, m_SizeRecv(0)
	, m_DataSeqRecv(0)
	, m_DataTypeRecv(0)
	, m_DataSeqSend(0)
	, m_DataTypeSend(0)
{
	m_BuffSend = (char*)malloc(BUFF_FIFO_SIZE);
	m_BuffRecv = (char*)malloc(BUFF_FIFO_SIZE);
}

TcpClient::~TcpClient()
{
	free(m_BuffSend);
	free(m_BuffRecv);
}

void TcpClient::Start(void* arg)
{
#if TCP_CLIENT_ENABLE
	TaskRun::Start(arg);
#endif
}

void TcpClient::Run(void* arg)
{
	int addr_family = 0;
	int ip_protocol = 0;

	while (1)
	{
		if (addr_family != 0)
		{
			TaskRun::mSleep(5000);
		}

		struct sockaddr_in dest_addr;
		inet_pton(AF_INET, m_Ip.c_str(), &dest_addr.sin_addr);
		dest_addr.sin_family = AF_INET;
		dest_addr.sin_port	 = htons(m_Port);
		addr_family			 = AF_INET;
		ip_protocol			 = IPPROTO_IP;

		int sock = socket(addr_family, SOCK_STREAM, ip_protocol);
		if (sock < 0)
		{
			LOG("Unable to create socket: errno %d\n", errno);
			shutdown(sock, 0);
			close(sock);
			continue;
		}
		// LOG("Socket created, connecting to %s:%d\n", m_Ip.c_str(), m_Port);

		int err = connect(sock, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
		if (err != 0)
		{
			// LOG("Socket unable to connect: errno %d\n", errno);
			shutdown(sock, 0);
			close(sock);
			continue;
		}
		LOG("Successfully connected\n");

		m_SocketId = sock;

		while (1)
		{
			int rlen = recv(sock, m_BuffRecv, BUFF_FIFO_SIZE, 0);
			if (rlen < 0 || !SocketCheckAlive(sock))
			{
				LOG("Error occurred during sending: errno %d\n", errno);
				shutdown(sock, 0);
				close(sock);
				break;
			}

			m_BuffFifo.Append(m_BuffRecv, rlen);
			while (1)
			{
				if (m_Protocol.Unpack(m_BuffRecv, &m_SizeRecv, &m_DataSeqRecv, &m_DataTypeRecv, &m_BuffFifo) != 0)
				{
					break;
				}

				CommHandler* handler = CommHandler::Instance();
				handler->HandlerExec(m_DataTypeRecv, m_BuffRecv, m_SizeRecv);
			}
		}

		LOG("Shutting down socket and restarting...\n");
		shutdown(sock, 0);
		close(sock);
	}
}

void TcpClient::Send(uint16_t dataType, char* data, int size)
{
#if TCP_CLIENT_ENABLE
	m_DataTypeSend = dataType;
	m_DataSeqSend++;
	m_Protocol.Pack(m_BuffSend, &m_SizeSend, data, size, m_DataSeqSend, m_DataTypeSend);
	if (m_SizeSend > 0 && m_SizeSend < BUFF_FIFO_SIZE)
	{
		if (SocketCheckAlive(m_SocketId))
		{
			send(m_SocketId, m_BuffSend, m_SizeSend, 0);
		}
	}
#endif
}